package tabletask

import (
	"errors"
	"io"
	"net/http"
	"os"
	"time"

	"cvevulner/cve-timed-task/db_models"
	"cvevulner/cve-timed-task/util"
	"cvevulner/models"
	"github.com/astaxie/beego"
	"github.com/astaxie/beego/logs"
	"github.com/astaxie/beego/orm"
)

func DownloadOpenlookengYaml() (string, error) {
	filePath := beego.AppConfig.String("timedtask::packageDir") + "openlookeng_yaml/"
	fErr := util.MakeDir(filePath)
	if fErr != nil {
		return "", fErr
	}
	yamlFileName := filePath + "openlookeng_yaml.yaml"
	//download the yaml file
	downloadUrl := "https://gitee.com/openlookeng/community/raw/master/security/config/Third_Party_Open_Source_Software_List.yaml"
	resp, dErr := http.Get(downloadUrl)
	defer resp.Body.Close()
	if dErr != nil {
		logs.Error("get", downloadUrl, "error: ", dErr.Error())
		return "", dErr
	}
	if resp.StatusCode != http.StatusOK {
		logs.Error("get", downloadUrl, resp.Status)
		return "", errors.New(resp.Status)
	}
	saveFile, cErr := os.Create(yamlFileName)
	defer saveFile.Close()
	if cErr != nil {
		logs.Error("Create file error", yamlFileName, cErr.Error())
		return "", cErr
	}
	_, cErr = io.Copy(saveFile, resp.Body)
	if cErr != nil {
		logs.Error("copy resp.Body to file error", yamlFileName, cErr.Error())
		return "", cErr
	}
	return yamlFileName, nil
}

// StoreOpenLookengYaml parse dict
//Store yaml data to mysql
func StoreOpenlookengYaml(yamlData map[string]map[string]map[string]string, ormModel orm.Ormer) {
	if len(yamlData) > 0 {
		err := db_models.UpdateOpenlookengYamlOriginMark(ormModel)
		if err != nil {
			logs.Error("db_models.UpdateYamlOriginMarkLookeng:", err.Error())
			return
		}
		for RepoKey, RepoValue := range yamlData {
			for k, v := range RepoValue {
				if _, ok := v["version"]; !ok || v["version"] == "" {
					v["version"] = ""
				}
				//open transaction
				logs.Info("open transaction")
				tranErr := ormModel.Begin()
				if tranErr != nil {
					logs.Error(" Open transaction error:", tranErr.Error())
					continue
				}
				originData, sErr := db_models.SelectOpenlookengYamlOriginData(k, v["version"], RepoKey, ormModel)
				now := time.Now()
				if sErr != nil {
					if errors.Is(orm.ErrNoRows, sErr) {
						logs.Warn("error is orm.ErrNoRows, Create a record")
						openLookengYaml := &models.OpenLookengYaml{
							PackageName: k,
							Version:     v["version"],
							OriginUrl:   v["url"],
							Status:      1,
							CpeName:     v["cpeName"],
							CreateTime:  now.Format("2006-01-02 15:04:05"),
							UpdateTime:  now.Format("2006-01-02 15:04:05"),
							MarkBit:     1,
							Repo:        RepoKey,
						}
						sErr = db_models.InsertOpenlookengYamlOriginData(openLookengYaml, ormModel)
						if sErr != nil {
							logs.Error("db_models.InsertOpenlookengYamlOriginData error:", sErr.Error())
							tranErr = ormModel.Rollback()
							if tranErr != nil {
								logs.Error(" Rollback transaction error:", tranErr.Error())
							}
							continue
						}
						logs.Info("insert openLookengYaml success")
					} else {
						logs.Error("db_models.SelectOpenlookengYamlOriginData error:", sErr)
						tranErr = ormModel.Rollback()
						if tranErr != nil {
							logs.Error(" Rollback transaction error:", tranErr.Error())
						}
						continue
					}
				} else {
					logs.Info("database records, change the data")
					openLookengYaml := &models.OpenLookengYaml{
						Id:         originData.Id,
						OriginUrl:  v["url"],
						Status:     1,
						CpeName:    v["cpeName"],
						UpdateTime: now.Format("2006-01-02 15:04:05"),
						MarkBit:    1,
					}
					_, sErr = db_models.UpdateOpenlookengYamlOriginData(openLookengYaml, ormModel)
					if sErr != nil {
						logs.Error("db_models.UpdateOpenlookengYamlOriginData:", sErr.Error())
						tranErr = ormModel.Rollback()
						if tranErr != nil {
							logs.Error(" Rollback transaction error:", tranErr.Error())
						}
						continue
					}
				}
				packAgesData, rowsAffected, yErr := db_models.SelectYamlData(k, v["version"], ormModel)
				if yErr != nil {
					logs.Error("db_models.SelectYamlData error:", yErr)
					tranErr = ormModel.Rollback()
					if tranErr != nil {
						logs.Error(" Rollback transaction error:", tranErr.Error())
					}
					continue
				}
				if rowsAffected == 0 {
					logs.Warn("No record found, Create a record")
					var packId int64
					gitOpenEuler, dErr := db_models.SelectYamlLastData(ormModel)
					if dErr != nil {
						if errors.Is(orm.ErrNoRows, dErr) {
							packId = 10000000
						} else {
							logs.Error("db_models.SelectYamlLastData error:", dErr)
							tranErr = ormModel.Rollback()
							if tranErr != nil {
								logs.Error(" Rollback transaction error:", tranErr.Error())
							}
							continue
						}
					} else {
						if gitOpenEuler.PackageId >= 10000000 {
							packId = gitOpenEuler.PackageId + 1
						} else {
							packId = 10000000
						}
					}
					goe := &models.GitOpenEuler{
						PackageId:   packId,
						PackageName: k,
						Version:     v["version"],
						OriginUrl:   v["url"],
						CreateTime:  now,
						UpdateTime:  now,
						CpePackName: v["cpeName"],
						Status:      1,
					}
					lastId, iErr := models.InsertYamlData(goe)
					if iErr != nil {
						logs.Error("db_models.InsertYamlData error:", iErr)
						tranErr = ormModel.Rollback()
						if tranErr != nil {
							logs.Error(" Rollback transaction error:", tranErr.Error())
						}
						continue
					}
					gpi := &models.GitPackageInfo{
						GitId:       lastId,
						PackageName: k,
						Version:     v["version"],
						OriginUrl:   v["url"],
						CreateTime:  now,
						UpdateTime:  now,
						Decription:  "",
						Status:      0,
					}
					iErr = db_models.InsertYamlDetailData(gpi, ormModel)
					if iErr != nil {
						logs.Error("db_models.InsertYamlDetailData:", iErr.Error())
						tranErr = ormModel.Rollback()
						if tranErr != nil {
							logs.Error(" Rollback transaction error:", tranErr.Error())
						}
						continue
					}
				} else {
					if rowsAffected > 1 {
						for _, pv := range packAgesData[1:] {
							yErr = db_models.DeleteYamlOpenEulerDetailData(pv.GitId, ormModel)
							if yErr != nil {
								logs.Error("db_models.DeleteYamlOpenEulerDetailData:", yErr.Error())
								tranErr = ormModel.Rollback()
								if tranErr != nil {
									logs.Error(" Rollback transaction error:", tranErr.Error())
								}
								continue
							}
							yErr = db_models.DeleteYamlOpenEulerData(pv.GitId, ormModel)
							if yErr != nil {
								logs.Error("db_models.DeleteYamlOpenEulerData:", yErr.Error())
								tranErr = ormModel.Rollback()
								if tranErr != nil {
									logs.Error(" Rollback transaction error:", tranErr.Error())
								}
								continue
							}
						}
					}
					goe := &models.GitOpenEuler{
						OriginUrl:   v["url"],
						UpdateTime:  now,
						CpePackName: v["cpeName"],
						Status:      1,
						PackageId:   packAgesData[0].PackageId,
						PackageName: k,
						Version:     v["version"],
					}
					yErr = models.UpdateYamlData(goe)
					if yErr != nil {
						logs.Error("db_models.UpdateYamlData:", yErr.Error())
						tranErr = ormModel.Rollback()
						if tranErr != nil {
							logs.Error(" Rollback transaction error:", tranErr.Error())
						}
						continue
					}
					gpi := &models.GitPackageInfo{
						PackageName: k,
						Version:     v["version"],
						OriginUrl:   v["url"],
						UpdateTime:  now,
						Status:      0,
						GitId:       packAgesData[0].GitId,
					}
					yErr = db_models.UpdateYamlDetailData(gpi, ormModel)
					if yErr != nil {
						logs.Error("db_models.UpdateYamlDetailData", yErr.Error())
						tranErr = ormModel.Rollback()
						if tranErr != nil {
							logs.Error(" Rollback transaction error:", tranErr.Error())
						}
						continue
					}
				}
				tranErr = ormModel.Commit()
				if tranErr != nil {
					logs.Error(" Commit transaction error:", tranErr.Error())
					continue
				}
				logs.Info("Transaction committed successfully", k)
			}
		}
		err = db_models.DeleteOpenlookengYamlOriginMark(ormModel)
		if err != nil {
			logs.Error("db_models.DeleteOpenlookengYamlOriginMark error:", err.Error())
		}
	} else {
		logs.Warning("yamlData Is empty, nothing can be done")
	}

}

func ProcOpenlookengYaml() {
	ormModel := orm.NewOrm()
	yamlFileName, err := DownloadOpenlookengYaml()
	if err != nil {
		logs.Error("DownloadOpenlookengYaml error:", err.Error())
		return
	}
	yamlData, err := util.ParseYamlTypeTwo(yamlFileName)
	if err != nil {
		logs.Error("util.ParseYamlTypeTwo error: ", yamlFileName, err)
		return
	}
	StoreOpenlookengYaml(yamlData, ormModel)
	DeleteYamlData(ormModel)
}
